Генерация кода в Engee
Генерация Си-кода из моделей Engee
Введение в генерацию кода
Автоматическая генерация встраиваемого кода в корне меняет работу инженеров. Вместо написания тысяч строк кода вручную, инженеры автоматически генерируют промышленный код для повышения производительности, улучшения качества и стимулирования инноваций.
Используя генератор кода Engee, вы можете:
- разрабатывать приложения реального времени для процессоров с плавающей или фиксированной точкой;
- генерировать Си- или Verilog-код из моделей Engee;
- оптимизировать код для определенных процессорных архитектур;
- интегрировать сгенерированный код с кодом, написанным вручную (существующим или специализированным под процессор);
- использовать блок C Function для верификации сгенерированного Си- или Verilog-кода в режиме «Программа-в-контуре» (Software-in-the-Loop);
- профилировать и осуществлять верификацию встраиваемого кода на микроконтроллерах и DSP процессорах.
Генератор кода Engee генерирует быстрый, компактный, человекочитаемый, переносимый, независимый от Engee код, пригодный для промышленного применения.
Сгенерированный код можно использовать во внешней среде разработки для создания исполняемого объектного файла для встраиваемого процессора, отладочной платы или ПЛИС.
Генератор Си-кода для подсистемы
Для генерации Си-кода для подсистемы через графический интерфейс Engee нажмите правой кнопкой мыши на подсистему (блок Подсистема) и в появившемся контекстном меню выберите пункт Сгенерировать код:
Таким способом можно сгенерировать код из любой выбранной подсистемы в модели.
Сгенерированный код находится в директории с именем modelname_Subsystem_code в той же директории, где находится модель (либо в текущей директории, если модель еще не сохранялась на диск). Здесь modelname — имя модели, Subsystem — имя подсистемы, а code — суффикс, добавляемый к имени директории со сгенерированным кодом. Например, для подсистемы с именем Subsystem в модели newmodel_1:
После завершения генерации в окне диагностики модели
появится уведомление об успешной генерации кода.
Для генерации кода на языке Си в окне настроек модели во вкладке Генерация кода значение параметра Целевая платформа должно быть выбрано равным Си:
Внутренние функции в сгенерированном коде
Генератор кода Engee поддерживает виртуальные и атомарные подсистемы для генерации кода. Виртуальные подсистемы служат только для визуальной иерархии в модели, в то время как атомарные подсистемы представляют собой единый блок выполнения (или функцию в сгенерированном коде). Настройка в свойствах подсистемы Treat as atomic unit позволяет сделать подсистему виртуальной или атомарной.
Все вычисления внутри виртуальных подсистем встраиваются (инлайнятся) непосредственно в код объемлющей подсистемы или модели. Это может улучшить производительность сгенерированного кода, но ухудшить его читаемость.
Все вычисления внутри атомарных подсистем генерируются в отдельную функцию с именем подсистемы. Вызов этой функции осуществляется автоматически в коде подсистемы или модели верхнего уровня. Это может улучшить читаемость сгенерированного кода, но ухудшить производительность.
Параметры в сгенерированном коде
Параметры в блоках модели
В блоках модели параметры могут быть заданы:
- в виде чисел (например,
0.2); - в виде выражений (например,
0.2 + 0.7); - в виде имен переменных из рабочего пространства Engee (например,
Kp).
В сгенерированном коде параметры встраиваются (инлайнятся) с теми значениями, которые заданы в модели:
- Простые численные значения добавляются в код «как есть», и дополнительных действий не требуется;
- Выражения могут быть упрощены, и в коде окажется только конечное вычисленное значение.
Настраиваемые параметры
Некоторые параметры блоков могут быть сделаны настраиваемыми, чтобы их можно было изменять после генерации кода. Такие параметры сохраняются в структуре modelname_P, которая выглядит так:
- Если параметр задан через имя переменной (например,
Kp), то ее имя сохраняется в структуре:
struct P_modelname_T_ {
double Kp;
};
- Если параметр задан в виде выражения (например,
Kp + 1), то используется название параметра (например,Amplitude):
struct P_modelname_T_ {
double Amplitude;
};
copy icon
Copied!
Во всех случаях параметры в структуре modelname_P заполняются вычисленными значениями, доступными из модели. Например:
P_modelname_T modelname_P = {
0.5 // Значение параметра
};
Примечание
С помощью опции Поведение параметров по умолчанию в окне настроек модели можно управлять тем, какие параметры останутся настраиваемыми в сгенерированном коде:
- Встроенные — числовые значения напрямую вставляются в сгенерированный код и не добавляются в структуру.
- Настраиваемые — параметры добавляются в структуру даже при числовых значениях.
Генерируемые файлы
После построения модели в Engee и генерации ее кода автоматически создаются файлы, которые содержат реализацию модели на языке Си. Ниже приведено описание этих файлов и их назначение.
-
modelname.h— заголовочный файл, в котором описаны внешние интерфейсы модели. В его состав входят функции и структуры, необходимые для работы с моделью из внешнего кода:modelname_init— функция инициализации модели. Должна вызываться однократно;modelname_step— точка входа в модель. Содержит алгоритм модели — должна вызываться периодически в соответствии с шагом расчета модели;modelname_U— структура содержит внешние входные порты модели;modelname_Y— структура содержит внешние выходные порты модели;modelname_S— структура содержит внутренние состояния модели;modelname_P— структура содержит настраиваемые параметры модели.
-
modelname.c— файл с реализацией логики модели. Содержит определения всех переменных и реализацию ключевых функций модели:init()— функция инициализации, устанавливающая начальные значения переменных и состояний модели;step()— основная вычислительная функция. Выполняет расчет одного шага симуляции и обновляет выходные значения;term()— функция завершения работы модели.
-
model_data.c— файл с начальной инициализацией параметров модели. В нем задаются значения всех настраиваемых параметров, которые используются при выполнении модели. Например, амплитуда и частота сигнала, фаза, смещение, количество выборок и другие параметры. Эти глобальные параметры являются настраиваемыми, и их можно менять во время работы исполняемого кода. Это позволяет гибко управлять поведением модели без необходимости пересборки логики или редактирования основных функций. -
main.c— файл с примером использования модели. Это основной исполняемый файл, где показано, как инициализировать и вызывать модель из программы на языке Си. Содержит следующие функции:init()— функцию инициализации модели, вызывается один раз перед началом работы;step()— основную вычислительную функцию, выполняющую один шаг расчета модели. Обычно вызывается периодически, в соответствии с заданным шагом интегрирования;term()— функцию завершения работы модели.
Файл main.c не содержит алгоритм работы модели, а лишь демонстрирует, как корректно запускать и управлять ее выполнением в приложении с поддержкой Си-кода. Файл main.c следует использовать как шаблон для интеграции модели в пользовательские проекты.
Интеграция во внешнюю среду разработки
Чтобы использовать функции и структуры сгенерированного файла modelname.h во внешнем коде (написанном вручную), нужно выполнить следующие действия:
Подключить сгенерированный заголовочный файл:
#include "modelname.h"
Организовать однократный вызов сгенерированной функции init в своем main:
modelname_init();
Организовать периодический вызов сгенерированной функции step в своем main с нужным шагом:
modelname_step();
Организовать передачу входных и выходных данных в функцию step и из нее:
/* Устанавливаем значения входов */
modelname_U.input1 = 42;
/* Вызываем функцию step */
modelname_step();
/* Можно использовать modelname_Y.output1 */
Поддерживаемые типы данных
Типы данных в сгенерированном коде соответствуют типам данных в модели Engee.
Если пользователь не задал тип данных в модели, то используется тип данных с плавающей точкой двойной точности, соответствующий типу double в Си. Для логических операций или вычислений, формирующих булевые результаты (true или false), используется тип данных boolean, соответствующий восьмибитному беззнаковому типу данных в Си.
Многочастотные модели
Модели Engee могут быть многочастотными, то есть содержать несколько отличающихся частот дискретизации. Применяя блоки Переход между частотами дискретизации, а также задавая параметр Период дискретизации блоков и подсистем, пользователь может управлять тем, с каким шагом расчета работают блоки или группы блоков.
Генератор кода Engee поддерживает генерацию кода для многочастотных моделей. Для этого блоки группируются по частотам, и пользователь может управлять тем, в каком виде эти разные частоты представлены в сгенерированном коде.
Настройка Генерация многочастотного кода позволяет управлять режимом работы сгенерированного кода для многочастотной модели. «Однозадачный» код позволяет сгенерировать одну функцию step для модели. «Многозадачный» код позволяет сгенерировать несколько функций step для модели, каждая из которых соответствует определенной частоте дискретизации.
Комментарии в сгенерированном коде
По умолчанию в сгенерированном коде содержатся комментарии, позволяющие проследить код до блоков в модели.
Настройка Включить комментарии в панели Генерация кода позволяют управлять комментариями в сгенерированном коде и при необходимости отключить их полностью.
Управление именами сигналов
Генератор кода использует имена сигналов, заданные пользователем в модели, чтобы сделать сгенерированный код более понятным и легким для сопоставления с моделью.
Если сигнал (выход блока) имеет имя, то в сгенерированном коде для этого сигнала будет использоваться то же имя переменной. Это возможно, только если сигнал не был оптимизирован и не содержит символов, которые не поддерживаются в Си-коде.
Поддерживаемые блоки
Генератором кода Engee поддерживается большое число библиотечных блоков. Список поддерживаемых блоков приведен в Документации. С каждым релизом Engee этот список пополняется новыми блоками.
Практическая часть
✏️Задание 1
Создайте модель, состоящую из блоков генератора синусоиды и заглушки, и сгенерируйте Си-код из модели:
- Создайте новую модель. Добавьте в модель блоки Генератор синусоиды и Заглушка и соедините их между собой. Параметры блоков оставьте равными по умолчанию.
- Зажав клавишу Shift, выделите блок Генератор синусоиды и нажмите на три точки. В появившемся меню выберите пункт Подсистема.
В результате выделенный блок будет помещен в подсистему. Переименуйте подсистему в SubSystem.
-
В окне настроек модели проверьте, что во вкладке Генерация кода значение параметра Целевая платформа выбрано равным
Си. -
Сохраните модель в папке данного курса под именем
sinewave.engee. -
Нажмите правой кнопкой мыши на подсистему и в появившемся контекстном меню выберите пункт Сгенерировать код.
В результате в текущей папке должна появиться папка sinewave_SubSystem_code, в которой будут находиться три файла:
main.c;sinewave_SubSystem.c;sinewave_SubSystem.h.
Решение
Модель:
Подсистема SubSystem:
Файл main.c:
#include "sinewave_SubSystem.h"
void do_step(void)
{
static bool OverrunFlag = false;
/* Check for overrun */
if (OverrunFlag) {
return;
}
OverrunFlag = true;
/* Step the model */
sinewave_SubSystem_step();
/* Indicate task complete */
OverrunFlag = false;
}
int main(int argc, char *argv[])
{
(void) argc;
(void) argv;
/* Initialize model */
sinewave_SubSystem_init();
while (1) {
/* Perform application tasks here */
}
/* Terminate model */
sinewave_SubSystem_term();
return 0;
}
Файл Sinewave_SubSystem.c:
#include "sinewave_SubSystem.h"
/* External outputs */
Ext_sinewave_SubSystem_Y sinewave_SubSystem_Y;
/* Block states */
Ext_sinewave_SubSystem_S sinewave_SubSystem_S;
/* Timings */
Ext_sinewave_SubSystem_T sinewave_SubSystem_T;
/* Model initialize function */
void sinewave_SubSystem_init() {
sinewave_SubSystem_T.clockTick0 = 0;
sinewave_SubSystem_T.clockTick1 = 0;
sinewave_SubSystem_T.stepSize0 = 0.01;
sinewave_SubSystem_T.t = 0;
sinewave_SubSystem_S.Sin_lastSin = 0.0;
sinewave_SubSystem_S.Sin_lastCos = 1.0;
}
/* Model terminate function */
void sinewave_SubSystem_term() {
/* (no terminate code required) */
}
/* Model step function */
void sinewave_SubSystem_step() {
double Sin;
double Sin_lastSin_tmp;
/* Sin: /Генератор синусоиды incorporates:
*/
Sin =
((sinewave_SubSystem_S.Sin_lastSin * 0.9999500004166653
+ sinewave_SubSystem_S.Sin_lastCos * -0.009999833334166664) * 0.9999500004166653 +
(sinewave_SubSystem_S.Sin_lastCos * 0.9999500004166653
- sinewave_SubSystem_S.Sin_lastSin * -0.009999833334166664) * 0.009999833334166664) * 1.0 + 0.0;
/* Outport: /Out1 incorporates:
* Sin: /Генератор синусоиды
*/
sinewave_SubSystem_Y.Out1 = Sin;
Sin_lastSin_tmp = sinewave_SubSystem_S.Sin_lastSin;
sinewave_SubSystem_S.Sin_lastSin =
sinewave_SubSystem_S.Sin_lastSin * 0.9999500004166653
+ sinewave_SubSystem_S.Sin_lastCos * 0.009999833334166664;
sinewave_SubSystem_S.Sin_lastCos =
sinewave_SubSystem_S.Sin_lastCos * 0.9999500004166653
- Sin_lastSin_tmp * 0.009999833334166664;
sinewave_SubSystem_T.t = ((++sinewave_SubSystem_T.clockTick0)) * sinewave_SubSystem_T.stepSize0;
{
sinewave_SubSystem_T.clockTick1++;
}
}
Файл sinewave_Subsystem.h:
#ifndef HEADER_sinewave_SubSystem_h
#define HEADER_sinewave_SubSystem_h
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include <float.h>
#include <limits.h>
#include <string.h>
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
typedef struct {
double Out1; /* /Out1 */
} Ext_sinewave_SubSystem_Y;
/* External outputs */
extern Ext_sinewave_SubSystem_Y sinewave_SubSystem_Y;
typedef struct {
double Sin_lastSin; /* /Генератор синусоиды */
double Sin_lastCos; /* /Генератор синусоиды */
} Ext_sinewave_SubSystem_S;
/* Block states */
extern Ext_sinewave_SubSystem_S sinewave_SubSystem_S;
typedef struct {
uint32_t clockTick0;
double stepSize0;
uint32_t clockTick1;
double t;
} Ext_sinewave_SubSystem_T;
/* Timings */
extern Ext_sinewave_SubSystem_T sinewave_SubSystem_T;
/* Model entry point functions */
extern void sinewave_SubSystem_init();
extern void sinewave_SubSystem_step();
extern void sinewave_SubSystem_term();
/* Here is the system hierarchy for this model:
*
* 'sinewave_SubSystem' : '/'
*/
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* HEADER_sinewave_SubSystem_h */
✏️Задание 2
Создайте модель, которая складывает две синусоиды, и сгенерируйте Си-код из модели:
- Создайте копию модели, построенной в предыдущем Задании, и назовите ее
two_sinewaves.engee. - Модифицируйте модель: зайдите в подсистему и добавьте в нее еще один блок Генератор синусоиды и блок Сложение. Соедините выходные сигналы генераторов синусоиды со входами блока Сложение, а выходной сигнал блока Сложение – со входом блока Out1.
- В окне настроек нового блока Генератор синусоиды-1 задайте значение параметра Угловая частота равным
2.0рад/с. - Выйдите из подсистемы. Нажмите правой кнопкой мыши на подсистему и в появившемся контекстном меню выберите пункт Сгенерировать код.
В результате в текущей папке должна появиться папка two_sinewaves_SubSystem_code, в которой будут находиться три файла:
main.c;two_sinewaves_SubSystem.c;two_sinewaves_SubSystem.h.
Решение
Подсистема SubSystem:
Файл main.c:
#include "two_sinewaves_SubSystem.h"
void do_step(void)
{
static bool OverrunFlag = false;
/* Check for overrun */
if (OverrunFlag) {
return;
}
OverrunFlag = true;
/* Step the model */
two_sinewaves_SubSystem_step();
/* Indicate task complete */
OverrunFlag = false;
}
int main(int argc, char *argv[])
{
(void) argc;
(void) argv;
/* Initialize model */
two_sinewaves_SubSystem_init();
while (1) {
/* Perform application tasks here */
}
/* Terminate model */
two_sinewaves_SubSystem_term();
return 0;
}
Файл two_sinewaves_SubSystem.c:
#include "two_sinewaves_SubSystem.h"
/* External outputs */
Ext_two_sinewaves_SubSystem_Y two_sinewaves_SubSystem_Y;
/* Block states */
Ext_two_sinewaves_SubSystem_S two_sinewaves_SubSystem_S;
/* Timings */
Ext_two_sinewaves_SubSystem_T two_sinewaves_SubSystem_T;
/* Model initialize function */
void two_sinewaves_SubSystem_init() {
two_sinewaves_SubSystem_T.clockTick0 = 0;
two_sinewaves_SubSystem_T.clockTick1 = 0;
two_sinewaves_SubSystem_T.stepSize0 = 0.01;
two_sinewaves_SubSystem_T.t = 0;
two_sinewaves_SubSystem_S.Sin_lastSin = 0.0;
two_sinewaves_SubSystem_S.Sin_lastCos = 1.0;
two_sinewaves_SubSystem_S.Sin_1_lastSin = 0.0;
two_sinewaves_SubSystem_S.Sin_1_lastCos = 1.0;
}
/* Model terminate function */
void two_sinewaves_SubSystem_term() {
/* (no terminate code required) */
}
/* Model step function */
void two_sinewaves_SubSystem_step() {
double Sin;
double Sin_lastSin_tmp;
double Sin_1;
double Sin_1_lastSin_tmp;
double Sum;
/* Sin: /Генератор синусоиды incorporates:
*/
Sin =
((two_sinewaves_SubSystem_S.Sin_lastSin * 0.9999500004166653
+ two_sinewaves_SubSystem_S.Sin_lastCos * -0.009999833334166664) * 0.9999500004166653 +
(two_sinewaves_SubSystem_S.Sin_lastCos * 0.9999500004166653
- two_sinewaves_SubSystem_S.Sin_lastSin * -0.009999833334166664) * 0.009999833334166664) * 1.0 + 0.0;
/* Sin: /Генератор синусоиды-1 incorporates:
*/
Sin_1 =
((two_sinewaves_SubSystem_S.Sin_1_lastSin * 0.9998000066665778
+ two_sinewaves_SubSystem_S.Sin_1_lastCos * -0.01999866669333308) * 0.9998000066665778 +
(two_sinewaves_SubSystem_S.Sin_1_lastCos * 0.9998000066665778
- two_sinewaves_SubSystem_S.Sin_1_lastSin * -0.01999866669333308) * 0.01999866669333308) * 1.0 + 0.0;
/* Sum: /Сложение incorporates:
* Sin: /Генератор синусоиды
* Sin: /Генератор синусоиды-1
*/
Sum = Sin + Sin_1;
/* Outport: /Out1 incorporates:
* Sum: /Сложение
*/
two_sinewaves_SubSystem_Y.Out1 = Sum;
Sin_lastSin_tmp = two_sinewaves_SubSystem_S.Sin_lastSin;
two_sinewaves_SubSystem_S.Sin_lastSin =
two_sinewaves_SubSystem_S.Sin_lastSin * 0.9999500004166653
+ two_sinewaves_SubSystem_S.Sin_lastCos * 0.009999833334166664;
two_sinewaves_SubSystem_S.Sin_lastCos =
two_sinewaves_SubSystem_S.Sin_lastCos * 0.9999500004166653
- Sin_lastSin_tmp * 0.009999833334166664;
Sin_1_lastSin_tmp = two_sinewaves_SubSystem_S.Sin_1_lastSin;
two_sinewaves_SubSystem_S.Sin_1_lastSin =
two_sinewaves_SubSystem_S.Sin_1_lastSin * 0.9998000066665778
+ two_sinewaves_SubSystem_S.Sin_1_lastCos * 0.01999866669333308;
two_sinewaves_SubSystem_S.Sin_1_lastCos =
two_sinewaves_SubSystem_S.Sin_1_lastCos * 0.9998000066665778
- Sin_1_lastSin_tmp * 0.01999866669333308;
two_sinewaves_SubSystem_T.t = ((++two_sinewaves_SubSystem_T.clockTick0)) * two_sinewaves_SubSystem_T.stepSize0;
{
two_sinewaves_SubSystem_T.clockTick1++;
}
}
Файл two_sinewaves_SubSystem.h:
#ifndef HEADER_two_sinewaves_SubSystem_h
#define HEADER_two_sinewaves_SubSystem_h
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include <float.h>
#include <limits.h>
#include <string.h>
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
typedef struct {
double Out1; /* /Out1 */
} Ext_two_sinewaves_SubSystem_Y;
/* External outputs */
extern Ext_two_sinewaves_SubSystem_Y two_sinewaves_SubSystem_Y;
typedef struct {
double Sin_lastSin; /* /Генератор синусоиды */
double Sin_lastCos; /* /Генератор синусоиды */
double Sin_1_lastSin; /* /Генератор синусоиды-1 */
double Sin_1_lastCos; /* /Генератор синусоиды-1 */
} Ext_two_sinewaves_SubSystem_S;
/* Block states */
extern Ext_two_sinewaves_SubSystem_S two_sinewaves_SubSystem_S;
typedef struct {
uint32_t clockTick0;
double stepSize0;
uint32_t clockTick1;
double t;
} Ext_two_sinewaves_SubSystem_T;
/* Timings */
extern Ext_two_sinewaves_SubSystem_T two_sinewaves_SubSystem_T;
/* Model entry point functions */
extern void two_sinewaves_SubSystem_init();
extern void two_sinewaves_SubSystem_step();
extern void two_sinewaves_SubSystem_term();
/* Here is the system hierarchy for this model:
*
* 'two_sinewaves_SubSystem' : '/'
*/
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* HEADER_two_sinewaves_SubSystem_h */
Внимание!
Пока не удаляйте файлы с моделями sinewave.engee и two_sinewaves.engee. Они вам понадобятся для выполнения упражнений в Разделе 3.
Тест для получения сертификата
Если вы впервые проходите тестирование на платформе Этюд по ссылке из Engee, то, нажав на ссылку ниже, вы увидите следующее диалоговое окно:
Нажмите на кнопку «Предоставить», чтобы авторизоваться на платформе Этюд с данными вашего профиля Engee ID.
Если вы раньше уже проходили тестирование по ссылке из Engee, то ваша авторизация выполнится автоматически и тестирование начнется сразу же после нажатия на ссылку.